package com.bnz.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.bnz.search.entity.ItemEntity;
import com.bnz.search.entity.ItemVo;
import com.bnz.search.service.ItemService;
import com.bnz.utils.Query;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Native;
import java.sql.Struct;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 类名：
 * 作者：WF
 * 功能：
 */
@Service
public class ItemServiceImpl implements ItemService {

	@Autowired
	private ElasticsearchRestTemplate restTemplate;
	@Autowired
	private StringRedisTemplate redisTemplate;
	/**
	 * 1、商品查询
	 * @param vo
	 * @return
	 */
	@Override
	public Map<String, Object> search(ItemVo vo) {
		//1.1、定义返回Map结果对象
		Map<String, Object> resultMap = new HashMap<>();
		//1.2、查询中的关键字
		if(StringUtils.isBlank(vo.getKeywords())){
			vo.setKeywords("");
		}
		//1.3、定义NativeSearchQueryBuilder
		NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
		//1.3.1、定义多个关键字的查询
		searchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(vo.getKeywords(),"title","brand","category"));
		//1.3.2、定义高亮查询
		searchQueryBuilder.withHighlightBuilder(new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>"));
		//1.3.3、进行分类的分组查询
		searchQueryBuilder.addAggregation(AggregationBuilders.terms("categoryGroup").field("category.keyword"));

		//1.4、定义组合查询对象
		//1.4.1、定义BoolQueryBuilder
		BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
		//1.4.2、添加过滤查询条件
		//1.4.2.1、分类查询
		if(StringUtils.isNotBlank(vo.getCategory())){
			boolQueryBuilder.filter(QueryBuilders.termQuery("category.keyword",vo.getCategory()));
		}
		//1.4.2.2、品牌查询
		if(StringUtils.isNotBlank(vo.getBrand())){
			boolQueryBuilder.filter(QueryBuilders.termQuery("brand.keyword",vo.getBrand()));
		}
		//1.4.2.3、规格以及规格选项的查询
		if(vo.getSpec() != null && vo.getSpec().size() > 0){
			List<String> spec = vo.getSpec();
			for (String s : spec) {
				String[] split = s.split(":");          // 拆分后：split[0]: 规格名字 split[1]: 规格选项的值
				boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + split[0] + ".keyword",split[1]));
			}
		}
		//1.4.2.4、价格区间的查询
		if(StringUtils.isNotBlank(vo.getPrice())){
			// ①拆分价格区间为最小价格与最大价格（用 - 隔开）
			String[] split = vo.getPrice().split("-");
			// ②处理特殊情况
			if(split[1].equals("*")){
				boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(split[0]));
			}else{
				boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gt(split[0]).lte(split[1]));
			}
		}
		//1.4.2.5、进行价格排序
		if(StringUtils.isNotBlank(vo.getSort())){
			//①拆分排序字段（用：隔开）
			String[] split = vo.getSort().split(":");       // split[0]: 排序字段 split[1]: 排序状态（asc/desc）
			//②开始排序
			if(split[1].equals("asc")){
				searchQueryBuilder.withSort(SortBuilders.fieldSort(split[0]).order(SortOrder.ASC));
			}else{
				searchQueryBuilder.withSort(SortBuilders.fieldSort(split[0]).order(SortOrder.DESC));
			}
		}
		//1.5、进行分页查询
		int page = vo.getPage();
		int pagesize = vo.getPageSize();


		//1.6、将上面的BoolQueryBuilder与NativeSearchQueryBuilder进行绑定
		searchQueryBuilder.withFilter(boolQueryBuilder);

		searchQueryBuilder.withPageable(PageRequest.of(page-1,pagesize));

		//1.7、开始搜索查询
		SearchHits<ItemEntity> searchHits = restTemplate.search(searchQueryBuilder.build(), ItemEntity.class, IndexCoordinates.of("item"));
		long total = searchHits.getTotalHits();
		//1.8、计算总页数
		long totalPage = (long) Math.ceil(total/(double)pagesize);

		//1.9、得到搜索查询后的碰撞对象
		//1.9.1、定义查询到商品的高亮集合
		List<ItemEntity> highlightEntries = new ArrayList<>();
		List<SearchHit<ItemEntity>> searchHitList = searchHits.getSearchHits();
		for (SearchHit<ItemEntity> entitySearchHit : searchHitList) {
			//1.9.1.1、得到高亮查询数据
			List<String> title = entitySearchHit.getHighlightField("title");
			//1.9.1.2、得到高亮查询原始对象
			ItemEntity itemEntity = entitySearchHit.getContent();
			//1.9.1.3、修改高亮查询原始对象的标题部分
			if(!CollectionUtils.isEmpty(title)) {
				itemEntity.setTitle(title.get(0));
			}
			//1.9.1.4、将上面经过修改后的高亮对象放到高亮集合中
			highlightEntries.add(itemEntity);
		}
		//1.9.2、取得分组的数据
		Aggregations aggregations = searchHits.getAggregations();
		//1.9.3、得到某个具体的分组
		ParsedStringTerms categoryGroup = aggregations.get("categoryGroup");
		//1.9.4、得到分组下的分桶数据
		List<? extends Terms.Bucket> buckets = categoryGroup.getBuckets();
		//1.9.5、遍历所有的分桶，其中的key就是分类的名字
		for (Terms.Bucket bucket : buckets) {
			String key = bucket.getKeyAsString();
			System.out.println("key = " + key);
		}
		//另一种方式：
		List<String> categoryList = buckets.stream().map(m -> m.getKeyAsString()).collect(Collectors.toList());
		// 定义用户点击的某个分类
		String category = "";
		//1.9.6、处理分类
		//1.9.6.1、前台传入的category为null（空的）
		String cate = "";
		Map brandAndSpecList = new HashMap();
		if(vo != null ){
			cate = vo.getCategory();
		}
		if(!CollectionUtils.isEmpty(categoryList)){
			if(StringUtils.isBlank(cate)){
				cate = categoryList.get(0);
			}
			//1.9.6.2、根据分类名字查询品牌及规格列表
			brandAndSpecList = findBrandAndSpec(cate);
		}else{
			brandAndSpecList.put("specList",new ArrayList<>());
			brandAndSpecList.put("brandList",new ArrayList<>());
		}

		//1.9.7、将分类、当前分页记录集合rows，总记录数，总页数totalPage分别放到大map中
		resultMap.put("categoryList",categoryList);
		resultMap.put("rows",highlightEntries);
		resultMap.put("total",total);
		resultMap.put("totalPage",totalPage);
		resultMap.putAll(brandAndSpecList);
		return resultMap;
	}

	//2、在redis中根据分类名找到模板id，再根据模板id找到品牌及规格列表
	private Map findBrandAndSpec(String cate) {
		//2.1、根据分类名找到模板id
		String typeId = (String) redisTemplate.boundHashOps("itemCats").get(cate);
		//2.2、根据模板id找到品牌及规格列表
		String brandListStr = (String) redisTemplate.boundHashOps("brandList").get(typeId);
		//2.2.1、将上面的品牌列表字符串转换为集合
		List<Map> brandList = JSON.parseArray(brandListStr,Map.class);
		//2.3、根据模板id找到规格及规格选项列表
		String specListStr = (String) redisTemplate.boundHashOps("specList").get(typeId);
		//2.3.1、将规格及规格选项列表转换为集合
		List<Map> specList = JSON.parseArray(specListStr, Map.class);
		//2.4、定义返回结果Map集合
		Map map = new HashMap();
		//2.5、将品牌及规格列表放到map集合当中
		map.put("brandList",brandList);
		map.put("specList",specList);
		//2.6、返回存入数据了的map集合对象
		return map;
	}


}
